/*
6.基于FFMPEG+SDL2播放音频
https://blog.csdn.net/u011003120/article/details/81950045
gcc -m32 -o demo audioDecodePlay2.c -lavformat -lavcodec -lswscale -lavutil -lswresample -lSDL2 -lm -lpthread -lz `pkg-config --cflags --libs sdl2`
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"
#include "SDL2/SDL.h"

//#define debug_msg(fmt, args...) printf("--->[%s,%d]  " fmt "\n\n", __FUNCTION__, __LINE__, ##args)

#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio  
                                     //48000 * (32/8)

unsigned int audioLen = 0;
unsigned char *audioChunk = NULL;
unsigned char *audioPos = NULL;

void fill_audio(void * udata, Uint8 * stream, int len)
{
    SDL_memset(stream, 0, len);

    if (audioLen == 0)
        return;

    len = (len>audioLen?audioLen:len);

    SDL_MixAudio(stream,audioPos,len,SDL_MIX_MAXVOLUME);

    audioPos += len;
    audioLen -= len;
}

int test_audio_2_play()
{
    AVFormatContext *pFortCtx = NULL;
    AVCodecContext *pCodecCtx = NULL;
    AVCodec *pCodec = NULL;
    AVPacket *pPkt = NULL;
    AVFrame*pFrame = NULL;
    struct SwrContext *pSwrCtx = NULL;
    SDL_AudioSpec wantSpec;

    //FILE* outFile = fopen("output.pcm", "wb");
    char inFile[] = "/home/jim/video_files/s320.mp4";

    int ret = -1;
    int audioIndex = -1;
    int i = 0;
    int got_picture = -1;

    uint64_t out_chn_layout = AV_CH_LAYOUT_STEREO;  //通道布局 输出双声道
    enum AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16; //声音格式
    int out_sample_rate=44100;   //采样率
    int out_nb_samples = -1;   
    int out_channels = -1;        //通道数
    int out_buffer_size = -1;   //输出buff
    unsigned char *outBuff = NULL;

    uint64_t in_chn_layout = -1;  //通道布局 

    struct SwrContext *au_convert_ctx;

    av_register_all();

    pFortCtx = avformat_alloc_context();  

    if (avformat_open_input(&pFortCtx, inFile, NULL, NULL) != 0)   //open input file and read data into buf
    {
        printf("avformat_open_input error!\n");
        ret = -1;
        goto ERR_1;
    }

    if (avformat_find_stream_info(pFortCtx, NULL) < 0)   //find stream some info
    {
        printf("avformat_find_stream_info error!\n");
        ret = -1;
        goto ERR_1;
    }

    /* find audio index */
    for (i = 0; i < pFortCtx->nb_streams; i++)
    {
        if (pFortCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            audioIndex = i;
            break;
        }
    }

    if (-1 == audioIndex)
    {
        printf("can not find audio index!\n");
        ret = -1;
        goto ERR_1;
    }

    printf("------>audioIndex is %d\n", audioIndex);

    pCodecCtx = pFortCtx->streams[audioIndex]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (NULL == pCodec)
    {
        printf("can not find decoder!\n");
        ret = -1;
        goto ERR_1;
    }
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Could not open codec.\n");
        ret = -1;
        goto ERR_1;
    }

    if (NULL == (pPkt = (AVPacket *)av_malloc(sizeof(AVPacket))))
    {
        printf("AV malloc failure.\n");
        ret = -1;
        goto ERR_2;
    }

    //out parameter
    out_nb_samples = pCodecCtx->frame_size;
    out_channels = av_get_channel_layout_nb_channels(out_chn_layout);
    out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples,  out_sample_fmt, 1);
    outBuff = (unsigned char *)av_malloc(MAX_AUDIO_FRAME_SIZE*2); //双声道
    printf("-------->out_buffer_size is %d\n",out_buffer_size);
    in_chn_layout = av_get_default_channel_layout(pCodecCtx->channels);

    pFrame = av_frame_alloc();
    //SDL
    wantSpec.freq = out_sample_rate;
    wantSpec.format = AUDIO_S16SYS;
    wantSpec.channels = out_channels;
    wantSpec.silence = 0;
    wantSpec.samples = out_nb_samples;
    wantSpec.callback = fill_audio;
    wantSpec.userdata = pCodecCtx;
    if (SDL_OpenAudio(&wantSpec, NULL) < 0)
    {
        printf("can not open SDL!\n");
        ret = -1;
        goto ERR_3;
    }


    //Swr
    au_convert_ctx=swr_alloc_set_opts(NULL,
    out_chn_layout,                                /*out*/
    out_sample_fmt,                              /*out*/
    out_sample_rate,                             /*out*/
    in_chn_layout,                                  /*in*/
    pCodecCtx->sample_fmt ,               /*in*/
    pCodecCtx->sample_rate,               /*in*/
    0, 
    NULL);

    swr_init(au_convert_ctx);

    SDL_PauseAudio(0);

    while(av_read_frame(pFortCtx, pPkt) >= 0)
    {
        if (pPkt->stream_index == audioIndex)
        {
            if (avcodec_decode_audio4(pCodecCtx, pFrame, &got_picture, pPkt) < 0)
            {
                printf("Error in decoding audio frame.\n");
                ret = -1;
                break;
            }

            if (got_picture > 0)
            {
                swr_convert(au_convert_ctx,&outBuff, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pFrame->data , pFrame->nb_samples);
                //fwrite(outBuff, 1, out_buffer_size, outFile); 
                while(audioLen > 0)
                    SDL_Delay(1); 

                audioChunk = (unsigned char *)outBuff;
                audioPos = audioChunk;
                audioLen = out_buffer_size;

            }
        }
        av_free_packet(pPkt);
    }

    SDL_CloseAudio();
    SDL_Quit();

    swr_free(&au_convert_ctx);
    ERR_3:
    av_free(outBuff);
    ERR_2:
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFortCtx);
    ERR_1:
    avformat_free_context(pFortCtx);
    //fclose(outFile);
    return ret;
}

int main(int argc, char *argv[])
{
    //test_audio_2_PCM();
    test_audio_2_play();

    return 0;
}
